Skiniko.prototype.stisimo = function() {
	var skiniko = this;

	this.
	setOpt({
		diafimisi: true,
		motd: true,
		emoticon: true,
		pssProsklisi: 1,
		pssSxesi: 1,
		pssSizitisi: 1,
	}).
	feredata(true).
	cpanelAdd();

	skiniko.pektisXromaGet();
	this.trexonInputFocus();
	$(document).on('mouseup', function(e) {
		skiniko.trexonInputFocus();
	});

	return this;
}

Skiniko.prototype.feredata = function(freska, delay) {
	return freska ?
		this.feredataFreska(delay)
	:
		this.feredataRequest(false)
	;
}

Skiniko.prototype.feredataFreska = function(delay) {
	var skiniko = this;

	this.
	skinikoReset().
	skinikoResetDOM();

	setTimeout(function() {
		skiniko.feredataRequest(true);
	}, delay === undefined ? 1000 : delay);

	return this;
}

Skiniko.prototype.feredataRequest = function(freska) {
	var skiniko = this, params = '';

	if (freska) params += 'freska';
	client.node('feredata', params, function(xhr) {
		var data;

		if (skiniko.feredataApopiraGet()) client.fyi.pano();
		skiniko.feredataDebug(xhr);
		if (skiniko.feredataSfalma(xhr)) return skiniko.feredataPali();

		data = new Feredata(xhr.responseText);
		if (data.isLathos()) return skiniko.feredataPali();
		if (data.isTimeout()) return skiniko.feredata();
		if (data.isParoximeno()) return;

		if (freska) skiniko.feredataSkiniko(data);
		else skiniko.feredataKinisi(data);

		skiniko.feredataApopiraClear().feredata();
	});

	return this;
}

Skiniko.prototype.feredataSkiniko = function(data) {
	this.pektis = data.hasOwnProperty('p') ? data.p : {};
	this.trapezi = data.hasOwnProperty('t') ? data.t : {};
	this.sinedria = data.hasOwnProperty('s') ? data.s : {};
	this.rebelos = data.hasOwnProperty('r') ? data.r : {};
	this.sizitisi = data.hasOwnProperty('z') ? data.z : [];
	this.skiniko2object().skinikoRefreshDOM();
	this.anazitisiArxiki();
	return this;
}

Skiniko.prototype.feredataKinisi = function(data) {
	var i, kinisi, idos, process;

	if (!data.hasOwnProperty('k')) {
		console.error('feredata: missing "k" property');
		return this;
	}

	for (i = 0; i < data.k.length; i++) {
		kinisi = new Kinisi(data.k[i]);
		idos = kinisi.idosFetch();

		process = 'processKinisi' + idos;
		if (!this[process]) globals.fatal(idos + ': απρόβλεπτο είδος κίνησης');

		this[process](kinisi);
	}

	return this;
}

Skiniko.prototype.feredataSfalma = function(xhr) {
	if (!xhr.sfalma) return false;

	client.fyi.epano(xhr.responseError);
	console.error('feredata: communication error:', xhr.responseError);
	return true;
}

Skiniko.prototype.feredataApopiraSet = function(apopira) {
	this.feredataApopira = apopira;
	return this;
}

Skiniko.prototype.feredataApopiraGet = function() {
	if (!this.hasOwnProperty('feredataApopira')) this.feredataApopiraSet(0);
	return this.feredataApopira;
}

Skiniko.prototype.feredataApopiraClear = function() {
	delete this.feredataApopira;
	return this;
}

Skiniko.prototype.feredataPali = function() {
	var skiniko = this, apopira = this.feredataApopiraGet(), delay = 50;

	if (apopira > 16) {
		this.rebelosGetDOM().find('.feredataWorking').remove();
		$('<div>').addClass('enimerosi epanisodos').html(
			'Παρουσιάστηκαν πολλά διαδοχικά σφάλματα στην επικοινωνία σας με τον' +
			'server. Δοκιμάστε επαναφόρτωση της σελίδας και αν τα προβλήματα ' +
			'επιμένουν δοκιμάστε <a target="_self" href="' + client.server +
			'login">επανείσοδο</a>. Αν πάλι υπάρχουν προβλήματα είναι ' +
			'πιθανόν ο server να βρίσκεται εκτός λειτουργίας.'
		).insertAfter('#basiPano').siromeno().anadisi();
		console.error('πολλά διαδοχικά σφάλματα στην επικοινωνία');
		client.ixos.error();
		client.fatal();
	}
	if (apopira > 10) delay = 500;
	else if (apopira > 6) delay = 200;
	else if (apopira > 3) delay = 100;
	else if (apopira == 1) this.rebelosGetDOM().find('.feredataWorkingIcon').attr({
		src: 'ikona/working/kokino.gif',
	});

	setTimeout(function() {
		skiniko.feredataApopiraSet(apopira + 1).feredataRequest(true);
	}, delay);

	return this;
}

Skiniko.prototype.feredataDebug = function(xhr) {
	if (xhr.hasOwnProperty('responseText')) console.log('feredata:', xhr.responseText);
	else console.error('feredata: no responseText');
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Feredata = function(data) {
	try {
		eval('globals.initObject(this, {' + data + '});');
	} catch (e) {
		globals.initObject(this, {
			'?': 'Παρελήφθησαν λανθασμένα δεδομένα',
		});
	}
}

Feredata.prototype.isLathos = function() {
	if (!this.hasOwnProperty('?')) return false;
	client.fyi.epano(this['?']);
	console.error('feredata: server error:', this['?']);
	return true;
}

Feredata.prototype.isTimeout = function() {
	return this.hasOwnProperty('=');
}

Feredata.prototype.isParoximeno = function() {
	return this.hasOwnProperty('~');
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Skiniko.prototype.skinikoResetDOM = function() {
	var minima = 'Παραλαβή δεδομένων';

	this.
	skinikoRefreshDOM().
	rebelosGetDOM().html($('<div>').addClass('feredataWorking').
	append($('<div>').addClass('feredataWorkingText').text(minima + '. Παρακαλώ περιμένετε…')).
	append($('<img>').addClass('feredataWorkingIcon').attr({
		src: 'ikona/working/default.gif',
		title: minima + '…',
	})));

	return this;
}

Skiniko.prototype.skinikoRefreshDOM = function() {
	if (!this.hasOwnProperty('DOM')) this.skinikoCreateDOM();

	this.
	refreshRebelosDOM().
	refreshTrapeziDOM().
	refreshPartidaDOM().
	refreshCpanelDOM().
	refreshProsklisiDOM().
	refreshSxesiDOM().
	refreshEmoticonDOM();

	this.sizitisiDOM.css({
		maxWidth: this.pssDOM.innerWidth() + 'px',
	});
	this.refreshSizitisiDOM();

	return this;
}

Skiniko.prototype.skinikoCreateDOM = function() {
	var skiniko = this, tr, hh, ha, h1;

	this.DOM = $('<table>').addClass('skinikoSkiniko').insertAfter('#basiPano');
	tr = $('<tr>').appendTo(this.DOM);

	this.kafenioDOM = $('<div>').addClass('skinikoStili skinikoKafenio').appendTo($('<td>').appendTo(tr));
	this.partidaDOM = $('<div>').addClass('skinikoStili skinikoPartida').appendTo($('<td>').appendTo(tr));
	this.cpanelDOM = $('<div>').addClass('skinikoStili skinikoCpanel selectOff').appendTo($('<td>').appendTo(tr));
	this.pssDOM = $('<div>').addClass('skinikoStili skinikoPss').appendTo($('<td>').css({width: '100%'}).appendTo(tr));
	this.emoticonDOM = $('<div>').addClass('skinikoStili skinikoEmoticon').appendTo($('<td>').appendTo(tr));

	this.rebelosDOM = $('<div>').addClass('skinikoRebelos').appendTo(this.kafenioDOM);
	this.trapeziDOM = $('<div>').addClass('skinikoTrapezi').appendTo(this.kafenioDOM);

	this.pssHeightAll = (ha = 500);
	this.pssHeightHeader = (hh = 30);
	this.pssHeightSection = (h1 = 136);

	this.pssHeaderDOM('Prosklisi').
	append($('<div>').addClass('pssHeaderTitlos').text('Προσκλήσεις'));
	this.prosklisiDOM = $('<div>').addClass('skinikoProsklisi').css({
		height: h1 + 'px',
		maxHeight: h1 + 'px'
	}).appendTo(this.pssDOM);

	this.pssHeaderDOM('Sxesi').
	append(this.anazitisiInputCreateDOM()).
	append(this.anazitisiResetCreateDOM()).
	append(this.anazitisiSxesiCreateDOM()).
	append(this.anazitisiKatastasiCreateDOM());
	this.sxesiDOM = $('<div>').addClass('skinikoSxesi').css({
		height: h1 + 'px',
		maxHeight: h1 + 'px',
	}).appendTo(this.pssDOM);

	this.pssHeaderDOM('Sizitisi').
	append(this.sizitisiInputCreateDOM()).
	append(this.sizitisiApostoliCreateDOM()).
	append(this.sizitisiResetCreateDOM()).
	append(this.sizitisiEnalagiCreateDOM());
	ha -= ((2 * h1) + (3 * hh));
	this.sizitisiDOM = $('<div>').addClass('skinikoSizitisi').css({
		height: ha + 'px',
		maxHeight: ha + 'px',
	}).appendTo(this.pssDOM);

	this.sizitisiKafenioDOM = $('<div>').addClass('sizitisiKafenio').appendTo(this.sizitisiDOM);
	this.sizitisiPartidaDOM = $('<div>').addClass('sizitisiPartida').appendTo(this.sizitisiDOM);
	this.sizitisiProepiskopisiDOM = $('<div>').addClass('sizitisiProepiskopisi').appendTo(this.sizitisiDOM);

	return this.DOM;
}

Skiniko.prototype.skinikoGetDOM = function() {
	return this.DOM;
}

Skiniko.prototype.rebelosGetDOM = function() {
	return this.rebelosDOM;
}

Skiniko.prototype.trapeziGetDOM = function() {
	return this.trapeziDOM;
}

Skiniko.prototype.partidaGetDOM = function() {
	return this.partidaDOM;
}

Skiniko.prototype.cpanelGetDOM = function() {
	return this.cpanelDOM;
}

Skiniko.prototype.prosklisiGetDOM = function() {
	return this.prosklisiDOM;
}

Skiniko.prototype.sxesiGetDOM = function() {
	return this.sxesiDOM;
}

Skiniko.prototype.sizitisiGetDOM = function() {
	return this.sizitisiDOM;
}

Skiniko.prototype.sizitisiKafenioGetDOM = function() {
	return this.sizitisiKafenioDOM;
}

Skiniko.prototype.sizitisiPartidaGetDOM = function() {
	return this.sizitisiPartidaDOM;
}

Skiniko.prototype.emoticonGetDOM = function() {
	return this.emoticonDOM;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Skiniko.prototype.refreshRebelosDOM = function() {
	var area = this.rebelosGetDOM().empty();

	this.rebelosWalk(function(login, rebelos) {
		rebelos.rebelosRefreshDOM(area);
	});

	return this;
}

Skiniko.prototype.refreshTrapeziDOM = function() {
	var area = this.trapeziGetDOM().empty();

	this.trapeziWalk(function(kodikos, trapezi) {
		trapezi.trapeziRefreshDOM(area);
	});

	return this;
}

// Η μέθοδος που ακολουθεί επαναδιαμορφώνει το DOM element που αφορά στην τρέχουσα παρτίδα.
// Πιο συγκεκριμένα, πρόκειται για την περιοχή δεξιά από την περιοχή καφενείου στην οποία
// εμφανίζονται τα στοιχεία τα ταυτότητας του τραπεζιού, και το ταμπλό με τα πιόνια κλπ.

Skiniko.prototype.refreshPartidaDOM = function(animation) {
	var dom = this.partidaGetDOM(), sinedria, trapezi, telefteaEnergia;

	// Αν δεν υπάρχει τρέχουσα συνεδρία, τότε βρισκόμαστε σε φάση αναδιαμόρφωσης τού
	// σκηνικού. Σ' αυτή την περίπτωση εμφανίζουμε κενό ταμπλό χωρίς να δίνουμε τη
	// δυνατότητα δημιουργίας νέου τραπεζιού.

	sinedria = this.sinedriaEgo();
	if (!sinedria) {
		dom.html(new Tablo().tabloRefreshDOM().tabloGetDOM());
		return this;
	}

	// Υπάρχει τρέχουσα συνεδρία οποτε επιχειρούμε να προσπελάσουμε το τρέχον τραπέζι.
	// Αν δεν υπάρχει τρέχον τραπέζι, εμφανίζουμε κενό ταμπλό με δυνατότητα δημιουργίας
	// νέου τραπεζιού.

	trapezi = this.trapeziGet(sinedria.trapeziGet());
	if (!trapezi) {
		dom.html(new Tablo({
			skiniko: this,
		}).tabloRefreshDOM().tabloGetDOM());
		return this;
	}

	// Υπάρχει τρέχον τραπέζι οπότε προσπελαύνουμε το ταμπλό και εφαρμόζουμε επάνω στο
	// ταμπλό τις ενέργειες των παικτών για την τελευταία παρτίδα.

	// Η εφαρμογή των ενργειών μπορεί να διαφοροποιηθεί όσον αφορά στην τελευταία ενέργεια
	// ανάλογα με το αν η παρούσα επαναδημιουργία έχει κληθεί με animation ή όχι. Στα batch
	// replays δεν έχουμε animation, αλλά στις online ενέργειες είναι καλό να «εμφανίζεται»
	// με κάποιον τρόπο η τελευταία ενέργεια.

	// Αν η παρούσα κλήση γίνεται με animation, αφαιρούμε προσωρινά την τελευταία ενέργεια
	// από την τρέχουσα παρτίδα εφόσον, βεβαίως, η ενέργεια αυτή σηκώενι animation.

	telefteaEnergia = animation ? trapezi.telefteaEnergiaPop() : null;

	// Είτε αφαιρέθηκε ενέργεια, είτε όχι επαναδιαμορφώνουμε το ταμπλό.

	dom.html(trapezi.tabloClearDOM().tabloGet().replay().tabloRefreshDOM().tabloGetDOM());

	// Αν έχουμε ενέργεια που έχει αφαιρεθεί, την επανεισάγουμε με την προσήκουσα μέθοδο
	// animation.

	if (telefteaEnergia) telefteaEnergia['animate' + telefteaEnergia.idosGet()](trapezi, this);
	else trapezi.oplismos();

	return this;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Energia.prototype.animateΚΙΝΗΣΗ = function(trapezi, skiniko) {
	var pioni, tetragono, position, tetragono1, position1, pioni1,
		dt, dl, energia = this, noumero = this.data.substr(0, 1),
		rowCol = this.data.substr(1, 2), tablo = trapezi.tabloGet();

	pioni = tablo.pioniGet(energia.pektis, noumero);
	tetragono = pioni.tetragonoGet();
	position = tetragono.tetragonoGetDOM().offset();

	tetragono1 = tablo.tetragonoGet(rowCol);
	position1 = tetragono1.tetragonoGetDOM().offset();
	pioni1 = tetragono1.pioniGet();

	dt = position1.top - position.top;
	dt = (dt >= 0 ? '+=' + dt : '-=' + (-dt)) + 'px';
	dl = position1.left - position.left;
	dl = (dl >= 0 ? '+=' + dl : '-=' + (-dl)) + 'px';

	if (pioni1) client.ixos.blioup();
	pioni.pioniGetDOM().anadisi().
	animate({
		top: dt,
		left: dl,
	}, 300, function() {
		var nikitis;

		if (!pioni1) client.ixos.tak();
		tablo.tabloGetDOM().remove();
		trapezi.partidaGet().energiaAdd(energia);
		delete trapezi.tablo;
		skiniko.partidaGetDOM().html(trapezi.tabloGet().replay().tabloRefreshDOM().tabloGetDOM());
		if (!pioni1) return trapezi.oplismos();

		tablo = trapezi.tabloGet();
		nikitis = tablo.nikitisGet();
		if (!nikitis) return trapezi.oplismos();

		pioni = tablo.pioniGet(energia.pektis, noumero);
		if (pioni) pioni.nikitisEndixi(nikitis, skiniko);
	});
}

Energia.prototype.animateΕΞΟΔΟΣ = function(trapezi, skiniko) {
	var energia = this, tetragono, tetragonoDOM, pioni, pioniDOM, offset, dexi,
	tablo = trapezi.tabloGet(),
	pektis = this.pektis,
	thesi = skiniko.sinedriaEgo().thesiGet(),

	// Ακολουθεί λίστα με τις εξόδους προς τα δεξιά. Ως δείκτης χρησιμοποιείται
	// ένα string που αποτελείται από:
	//
	//	Θέση θέασης παίκτη/θεατή.
	//
	//	Θέση παίκτη του οποίου το πιόνι εξέρχεται.
	//
	//	Δείκτης τετραγώνου διαφυγής (row, col).
	//
	// Θυμηθείτε ότι στην περίπτωση θέασης από τη θέση 2 το ταμπλό είναι ανεστραμμένο,
	// επομένως το τετράγωνο (1, 1) βρίσκεται επάνω δεξιά και το (6, 6) βρίσκεται
	// κάτω αριστερά.

	dexia = {
		'1166': 1,
		'1216': 1,
		'2211': 1,
		'2161': 1,
	};

	pioni = tablo.pioniGet(pektis, this.data);
	if (!pioni) return this;

	tetragono = pioni.tetragonoGet();
	if (!tetragono) return this;

	$('.tabloExodos').remove();
	dexi = dexia.hasOwnProperty(thesi + '' + pektis + '' + tetragono.rcGet());
	tetragonoDOM = tetragono.tetragonoGetDOM();
	tetragonoDOM.append($('<img>').
	addClass('tabloExodos tabloExodos' + (dexi ? 'Right' : 'Left')).attr({
		src: 'ikona/pexnidi/exodos.png',
	}));

	pioniDOM = pioni.pioniGetDOM().find('.pioniTetragonoIcon');
	offset = pioniDOM.position();
	pioniDOM.anadisi().css({
		top: offset.top + 'px',
		left: offset.left + 'px',
	}).animate({
		left: (dexia.hasOwnProperty(thesi + '' + pektis + '' + tetragono.rcGet()) ? '+' : '-') + '120px',
	}, 500, 'easeOutBounce', function() {
		var partida = trapezi.partidaGet();
		if (partida) partida.energiaAdd(energia);
		tablo.replay().epomenosClear();
		$('.pioniTetragonoIcon').siromenoOff();
		$(this).attr({
			src: 'ikona/pexnidi/pioniB.png',
		});
	});

	client.ixos.play(thesi == pektis ? 'applause.ogg' : 'bounce.ogg');
	return this;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Trapezi.prototype.tabloClearDOM = function() {
	var tablo = this.tabloGet();
	if (tablo.isDOM()) tablo.tabloGetDOM().remove();
	return this;
}

Trapezi.prototype.oplismos = function() {
	var pektis, tablo, epomenos;

	pektis = this.pektisEgo();
	if (!pektis) return this;

	tablo = this.tabloGet();
	if (!tablo) return this;

	switch (tablo.fasiGet()) {
	case 'ΣΤΗΣΙΜΟ':
	case 'ΠΑΙΧΝΙΔΙ':
		epomenos = tablo.epomenosGet();
		if (epomenos && (epomenos != pektis)) return this;

		tablo.pioniWalk(function(pioni) {
			if (pioni.pektisGet() == pektis) pioni.siromeno();
		});
		break;
	default:
		tablo.epomenosClear();
		break;
	}

	return this;
}

Trapezi.prototype.telefteaEnergiaPop = function() {
	var partida = this.partidaGet(), energia;

	if (!partida) return null;
	if (partida.energia.length < 1) return null;

	energia = partida.energia[partida.energia.length - 1];
	return energia['animate' + energia.idosGet()] ? partida.energia.pop() : null;
}

Skiniko.prototype.refreshCpanelDOM = function() {
	var
	skiniko = this,
	trexousa = this.cpanelOmadaGet(),
	sinedria = this.sinedriaEgo(),
	trapezi = this.trapeziEgo();

	// Εφόσον το σκηνικό διαθέτει control panel προχωρούμε στην εμφάνιση/απόκρυψη των
	// πλήκτρων της τρέχουσας ομάδας εργαλείων τού control panel.

	if (this.isCpanel()) this.cpanelWalk(function() {
		// Αν το πλήκτρο δεν ανήκει σε συγκεκριμένη ομάδα θεωρούμε ότι
		// ανήκει στην τρέχουσα, οπότε θα εμφανίζεται ανεξαρτήτως ομάδας
		// εφόσον, βεβαίως, περάσει από τυχόν αυτοέλεγχο που ίσως διαθέτει.

		var omada = this.isOmada() ? this.omadaGet() : trexousa;

		// Αν το πλήκτρο δεν ανήκει στην τρέχουσα ομάδα, αποκρύπτεται.

		if (omada != trexousa) this.apokripsi();

		// Το πλήκτρο ανήκει στην τρέχουσα ομάδα, επομένως θα δούμε αν διαθέτει
		// μέθοδο αυτοελέγχου. Αν το πλήκτρο στερείται τέτοιας μεθόδου, θεωρείται
		// αποδεκτό.

		else if (this.oxiCheck()) this.emfanisi();

		// Το πλήκτρο διαθέτει μέθοδο αυτοελέγχου οπότε ελέγχουμε αν ο συγκεριμένος
		// έλεγχος είναι επιτυχής.

		else if (this.checkGet().call(this, skiniko, sinedria, trapezi)) this.emfanisi();

		// Ο αυτοέλεγχος απέτυχε, οπότε το πλήκτρο δεν είναι αποδεκτό στην παρούσα
		// φάση.

		else this.apokripsi();
	});

	// Αν το σκηνικό είναι σε φάση κατά την οποία στερείται control panel, απλώς καθαρίζουμε
	// την αντίστοιχη περιοχή του DOM.

	else this.cpanelGetDOM().empty();

	return this;
}

Skiniko.prototype.refreshProsklisiDOM = function() {
	var skiniko = this;

	this.prosklisiGetDOM().empty();
	this.trapeziWalk(function(kodikos, trapezi) {
		trapezi.prosklisiWalk(function(kodikos, prosklisi) {
			prosklisi.prosklisiRefreshDOM(skiniko);
		});
	});

	return this;
}

// Η μέθοδος "refreshSxesiDOM" επαναδημιουργεί την περιοχή αποτελεσμάτων αναζήτησης.
// Ωστόσο, ΔΕΝ επαναλαμβάνει την αναζήτηση παρά χρησιμοποιεί τα τρέχοντα αποτελέσματα.

Skiniko.prototype.refreshSxesiDOM = function() {
	var dom = this.sxesiGetDOM(), anazitisi = this.anazitisiGet();

	dom.empty();
	anazitisi.epilogiWalk(function(login, epilogi) {
		dom.append(epilogi.epilogiGetDOM());
	});

	return this;
}

Skiniko.prototype.refreshSizitisiDOM = function() {
	this.
	refreshSizitisiKafenioDOM().
	refreshSizitisiPartidaDOM().
	sizitisiGetDOM().scrollKato();
	return this;
}

Skiniko.prototype.refreshSizitisiKafenioDOM = function() {
	var skiniko = this, area = this.sizitisiKafenioGetDOM().empty();

	this.sizitisiWalk(function(i, sizitisi) {
		sizitisi.sizitisiRefreshDOM(skiniko, area);
	});

	return this;
}

Skiniko.prototype.refreshSizitisiPartidaDOM = function() {
	var skiniko = this, area = this.sizitisiPartidaGetDOM().empty(), trapezi = this.trapeziEgo();

	if (trapezi) {
		trapezi.sizitisiWalk(function(i, sizitisi) {
			sizitisi.sizitisiRefreshDOM(skiniko, area);
		});
	}

	return this;
}

Skiniko.prototype.refreshEmoticonDOM = function() {
	this.emoticonGetDOM().text('EM');
	return this;
}

Skiniko.prototype.trapeziRefreshDOM = function(kodikos) {
	var trapezi = this.trapeziGet(kodikos);
	if (!trapezi) return this;

	if (trapezi.hasOwnProperty('DOM')) trapezi.trapeziRefreshDOM();
	else trapezi.trapeziRefreshDOM(this.trapeziGetDOM());

	return this;
}

Skiniko.prototype.rebelosRefreshDOM = function(pektis) {
	var rebelos = this.rebelosGet(pektis);
	if (!rebelos) return this;

	if (rebelos.hasOwnProperty('DOM')) rebelos.rebelosRefreshDOM();
	else rebelos.rebelosRefreshDOM(this.rebelosGetDOM());

	return this;
}

Skiniko.prototype.rebelosRemoveDOM = function(pektis) {
	var rebelos = this.rebelosGet(pektis);
	if (rebelos) rebelos.rebelosRemoveDOM();
	return this;
}

Skiniko.prototype.sinedriaEgo = function() {
	return this.sinedriaGet(client.session.pektis);
}

Skiniko.prototype.sinedriaEgoFetch = function() {
	var sinedria = this.sinedriaEgo();
	if (sinedria) return sinedria;
	globals.fatal('Δεν βρέθηκε δική μας συνεδρία στο σκηνικό');
}

Skiniko.prototype.pektisEgo = function() {
	return this.pektisGet(client.session.pektis);
}

Skiniko.prototype.pektisEgoFetch = function() {
	var pektis = this.pektisEgo();
	if (pektis) return pektis;
	globals.fatal('Δεν βρέθηκε ο δικός μας παίκτης στο σκηνικό');
}

Skiniko.prototype.trapeziEgo = function() {
	var sinedria = this.sinedriaEgo();
	if (!sinedria) return null;
	return this.trapeziGet(sinedria.trapeziGet());
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Sinedria.prototype.isEgo = function() {
	return this.pektisGet().isEgo();
}

Sinedria.prototype.oxiEgo = function() {
	return !this.isEgo();
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Trapezi.prototype.trapeziGetDOM = function() {
	var trapezi = this;

	if (this.hasOwnProperty('DOM')) return this.DOM;

	this.DOM = $('<div>').addClass('trapezi').
	append($('<div>').addClass('trapeziTablo').
		append($('<span>').addClass('trapeziData').
			attr({
				title: 'Είσοδος στο τραπέζι',
			}).
			append($('<div>').addClass('trapeziKodikos'))
		).
		append($('<span>').addClass('trapeziPektis trapeziPektis1')).
		append($('<span>').addClass('trapeziPektis trapeziPektis2'))
	).
	append($('<div>').addClass('trapeziTheatisArea'));

	return this.DOM;
}

Trapezi.prototype.trapeziTheatisGetDOM = function() {
	return this.trapeziGetDOM().find('.trapeziTheatisArea');
}

Trapezi.prototype.trapeziRefreshDOM = function(area) {
	var skiniko = this.skinikoFetch(), trapezi = this, dom = this.trapeziGetDOM();

	this.
	trapeziEnergoRefreshDOM().
	trapeziDataRefreshDOM().
	trapeziPektisRefreshDOM(1).
	trapeziPektisRefreshDOM(2).
	trapeziTheatisRefreshDOM();
	if (!area) return this;

	dom.find('.trapeziData').
	off('click').
	on('click', function(e) {
		e.stopPropagation();
		trapezi.trapeziEpilogi(skiniko.sinedriaEgo());
	})

	area.prepend(dom);
	return this;
}

Trapezi.prototype.trapeziEnergoRefreshDOM = function() {
	var skiniko = this.skinikoFetch();

	if (skiniko.sinedriaEgo().trapeziGet() == this.kodikosGet()) this.trapeziGetDOM().addClass('trapeziEnergo');
	else this.trapeziGetDOM().removeClass('trapeziEnergo');
	return this;
}

Trapezi.prototype.trapeziDataRefreshDOM = function() {
	var dom = this.trapeziGetDOM().find('.trapeziData');

	dom.find('.trapeziKodikos').text(this.kodikosGet());

	if (this.kalesmenosEgo()) dom.addClass('trapeziDataProsklisi');
	else dom.removeClass('trapeziDataProsklisi');

	return this;
}

Trapezi.prototype.trapeziPektisRefreshDOM = function(thesi) {
	var skiniko = this.skinikoFetch(), trapezi = this, sinedria, pektis, box;

	if (geister.oxiThesi(thesi)) return this;

	box = this.trapeziGetDOM().find('.trapeziPektis' + thesi).
	off('mouseenter mouseleave click').
	removeClass().
	addClass('trapeziPektis trapeziPektis' + thesi);

	pektis = this.pektisGet(thesi);
	if (!pektis) {
		box.addClass('keniThesi');
		pektis = this.telefteosPektis(thesi);

		if (pektis)
		box.
		text(pektis).
		addClass('fevgatos').
		// Δίνουμε δυνατότητα αποστολής πρόσκλησης ακόμη και σε φαντασματικούς
		// παίκτες, δηλαδή σε παίκτες που έχουν αποχωρήσει από το τραπέζι.
		pektisOplismos(skiniko, pektis);

		else
		box.
		html('&#8203;');

		return this;
	}

	box.
	html(pektis).
	addClass(this.apodoxiGet(thesi).isNai() ? 'apodoxiNai' : 'apodoxiOxi').
	pektisOplismos(skiniko, pektis);

	sinedria = skiniko.sinedriaGet(pektis);
	if (!sinedria) box.addClass('offline');
	else if (sinedria.trapeziGet() != this.kodikosGet()) box.addClass('alitevi');

	return this;
}

Trapezi.prototype.trapeziTheatisRefreshDOM = function() {
	var skiniko = this.skinikoFetch(), trapezi = this, theatisArea = this.trapeziTheatisGetDOM();

	theatisArea.empty();
	this.theatisWalk(function(pektis, theatis) {
		if (skiniko.oxiSinedria(pektis)) return;
		theatis.theatisRefreshDOM(skiniko, theatisArea);
	});

	return this;
}

Trapezi.prototype.theatisRemoveDOM = function(pektis) {
	var theatis = this.theatisGet(pektis);
	if (theatis) theatis.theatisRemoveDOM();
	return this;
}

Trapezi.prototype.trapeziEpilogi = function(sinedria) {
	var kodikos;

	if (!sinedria) return;

	kodikos = this.kodikosGet();
	if (sinedria.trapeziGet() == kodikos) return;

	client.fyi.pano('Είσοδος στο τραπέζι. Παρακαλώ περιμένετε…');
	client.node('trapeziEpilogi', 'trapezi=' + kodikos, function(xhr) {
		if (xhr.sfalma) return;
		client.fyi.epano(xhr.responseText);
	});
}

Trapezi.prototype.kalesmenosEgo = function() {
	return this.isKalesmenos(client.session.pektis);
}

Trapezi.prototype.pektisEgo = function() {
	return this.thesiPekti(client.session.pektis);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Theatis.prototype.theatisGetDOM = function() {
	if (!this.hasOwnProperty('DOM')) this.DOM = $('<div>').addClass('trapeziTheatis theatis');
	return this.DOM;
}

Theatis.prototype.theatisRefreshDOM = function(skiniko, area) {
	var pektis = this.pektisGet();

	this.theatisGetDOM().empty().
	append($('<div>').addClass('trapeziTheatisPektis').text(pektis)).
	append($('<div>').addClass('trapeziTheatisThesi').text(this.thesiGet())).
	pektisOplismos(skiniko, pektis).
	on('mouseenter', function(e) {
		e.stopPropagation();
		$(this).find('.trapeziTheatisThesi').finish().css({display: 'inline'});
	}).
	on('mouseleave', function(e) {
		e.stopPropagation();
		$(this).find('.trapeziTheatisThesi').finish().css({display: 'none'});
	});

	if (area) {
		area.prepend(this.DOM);
		this.thesiFlashDOM();
	}

	return this;
}

Theatis.prototype.thesiFlashDOM = function() {
	var
	dom = this.theatisGetDOM().find('.trapeziTheatisThesi'),
	timer = dom.data('flash');

	if (timer) clearTimeout(timer);
	dom.finish().css({display: 'inline'});
	dom.data('flash', setTimeout(function() {
		dom.finish().fadeOut(200);
	}, 700));
}

Theatis.prototype.theatisRemoveDOM = function(x) {
	if (this.hasOwnProperty('DOM')) this.theatisGetDOM().fadeOut(function() {
		$(this).remove();
	});

	delete this.DOM;
	return this;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Rebelos.prototype.rebelosGetDOM = function() {
	if (!this.hasOwnProperty('DOM')) this.DOM = $('<div>').addClass('rebelos');
	return this.DOM;
}

Rebelos.prototype.rebelosRefreshDOM = function(area) {
	var skiniko = this.skinikoFetch();

	this.rebelosGetDOM().text(this.pektisGet()).pektisOplismos(skiniko, this.pektisGet());
	if (area) area.prepend(this.DOM);
	return this;
}

Rebelos.prototype.rebelosRemoveDOM = function() {
	if (this.hasOwnProperty('DOM')) this.rebelosGetDOM().fadeOut(function() {
		$(this).remove();
	});

	delete this.DOM;
	return this;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Prosklisi.prototype.prosklisiGetDOM = function() {
	if (!this.hasOwnProperty('DOM'))
	this.DOM = $('<div>').addClass('prosklisi').attr({
		title: 'Αποδοχή πρόσκλησης',
	}).
	on('mouseenter', function(e) {
		e.stopPropagation();
		$(this).addClass('prosklisiTrexousa');
	}).
	on('mouseleave', function(e) {
		e.stopPropagation();
		$(this).removeClass('prosklisiTrexousa');
	});

	return this.DOM;
}

Prosklisi.prototype.ikodespotisEgo = function() {
	return this.apoGet().isEgo();
}

Prosklisi.prototype.kalesmenosEgo = function() {
	return this.prosGet().isEgo();
}

Prosklisi.prototype.prosklisiRefreshDOM = function(skiniko) {
	var
	prosklisi = this,
	ikodespotisEgo = this.ikodespotisEgo(),
	kalesmenosEgo = this.kalesmenosEgo(),
	dom;

	dom = this.prosklisiGetDOM().empty().
	append($('<img>').addClass('pssIcon prosklisiDeleteIcon').attr({
		src: 'ikona/misc/' + (ikodespotisEgo ? 'Xgreen.png' : 'Xred.png'),
		title: (ikodespotisEgo ? 'Ανάκληση' : 'Απόρριψη') + ' πρόσκλησης',
	}).on('click', function(e) {
		var trapezi, img = $(this);

		e.stopPropagation();

		trapezi = skiniko.trapeziGet(prosklisi.trapeziGet());
		if (!trapezi) return;
		if (!trapezi.prosklisiGet(prosklisi.kodikosGet())) return;

		$(this).data('src', $(this).attr('src')).attr({src: 'ikona/working/kokino.gif'});
		client.fyi.pano('Διαγραφή πρόσκλησης. Παρακαλώ περιμένετε…');
		client.node('prosklisiDiagrafi',
			'trapezi=' + prosklisi.trapeziGet(),
			'prosklisi=' + prosklisi.kodikosGet(),
		function(xhr) {
			var err = false;

			if (xhr.sfalma) err = true;
			else {
				client.fyi.epano(xhr.responseText);
				if (xhr.responseText) err = true;
			}

			if (err) img.attr({src: img.data('src')});
		});
	})).
	append($('<span>').addClass('prosklisiKodikos').text(this.kodikosGet()));

	if (kalesmenosEgo)
	dom.removeClass('prosklisiIkothen').
	append($('<span>').addClass('prosklisiApo').text(this.apoGet())).
	on('click', function(e) {
		var kodikos, trapezi, sinedria, thesi, img;

		e.stopPropagation();
		if (!prosklisi.kalesmenosEgo()) return;

		kodikos = prosklisi.trapeziGet();
		trapezi = skiniko.trapeziGet(kodikos);
		if (!trapezi) return;

		sinedria = skiniko.sinedriaEgo();
		if (!sinedria) return;

		// Εξετάζουμε πρώτα την περίπτωση κατά την οποία ο παίκτης
		// βρίσκεται ήδη στο τραπέζι της πρόσκλησης.

		if (sinedria.trapeziGet() == kodikos) {
			// Αν είναι ήδη παίκτης στο τραπέζι δεν έχουμε να
			// κάνουμε περαιτέρω ενέργειες.

			if (sinedria.isPektis()) return client.fyi.bpano("Παίζετε ήδη σ' αυτό το τραπέζι");

			// Ο παίκτης είναι θεατής στο τραπέζι, οπότε θα τον
			// κάνουμε παίκτη, αλλά πρέπει να υπάρχει ελεύθερη
			// θέση.

			if (!trapezi.keniThesi()) return client.fyi.spano('Δεν υπάρχει ελεύθερη θέση στο τραπέζι');
		}

		img = prosklisi.prosklisiGetDOM().find('.prosklisiDeleteIcon');
		if (img.length) img.data('src', img.attr('src')).attr({src: 'ikona/working/default.gif'});
		client.fyi.pano('Αποδοχή πρόσκλησης. Παρακαλώ περιμένετε…');
		client.node('prosklisiApodoxi', 'trapezi=' + kodikos, 'prosklisi=' + prosklisi.kodikosGet(),
		function(xhr) {
			if (img.length) img.attr({src: img.data('src')});
			if (xhr.sfalma) return client.ixos.error();
			client.fyi.spano(xhr.responseText);
		});
	});

	else
	dom.addClass('prosklisiIkothen').
	append($('<span>').addClass('prosklisiPros').text(this.prosGet()));

	dom.
	append($('<span>').addClass('prosklisiTrapezi').text(this.trapeziGet())).
	prependTo(skiniko.prosklisiGetDOM());

	return this;
}

Prosklisi.prototype.prosklisiRemoveDOM = function() {
	if (this.hasOwnProperty('DOM')) this.prosklisiGetDOM().fadeOut(function() {
		$(this).remove();
	});

	delete this.DOM;
	return this;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

jQuery.fn.pektisOplismos = function(skiniko, pektis) {
	return this.each(function() {
		$(this).

		off('mouseenter').
		on('mouseenter', function(e) {
			var sinedria = skiniko.sinedriaEgo(), trapezi;

			e.stopPropagation();

			$(this).
			addClass('pektisOplisate1').
			removeAttr('title');

			if (!sinedria) return;
			
			trapezi = skiniko.trapeziGet(sinedria.trapeziGet());
			if (!trapezi) return;

			if (!trapezi.thesiPekti(client.session.pektis)) return;
			if (trapezi.isKalesmenos(pektis)) return;

			$(this).
			addClass('pektisOplisate2').
			attr({title: 'Αποστολή πρόσκλησης για το τραπέζι ' + trapezi.kodikosGet()});
		}).

		off('mouseleave').
		on('mouseleave', function(e) {
			e.stopPropagation();

			$(this).
			removeClass('pektisOplisate1 pektisOplisate2');
		}).

		off('click').
		on('click', function(e) {
			var sinedria = skiniko.sinedriaEgo();

			e.stopPropagation();

			if (!sinedria) return;

			trapezi = skiniko.trapeziGet(sinedria.trapeziGet());
			if (!trapezi.thesiPekti(client.session.pektis)) return;

			if (trapezi.isKalesmenos(pektis)) return client.fyi.bpano('Ο παίκτης <span class="entono ble">' +
				pektis + '</span> έχει ήδη προσκληθεί στο τραπέζι <span class="entono ble">' +
				trapezi.kodikosGet() + '</span>');

			client.fyi.pano('Αποστολή πρόσκλησης. Παρακαλώ περιμένετε…');
			client.node('prosklisiApostoli', 'trapezi=' + trapezi.kodikosGet(), 'pros=' + pektis,
			function(xhr) {
				if (xhr.sfalma) return;
				client.fyi.epano(xhr.responseText);
			});
		});
	});
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Skiniko.prototype.pektisXromaGet = function(pektis) {
	if (pektis === undefined) pektis = client.session.pektis;
	if (!this.hasOwnProperty('xromaPektis')) this.xromaPektis = {};
	if (!this.xromaPektis.hasOwnProperty(pektis)) this.xromaPektis[pektis] = client.zebraPaletaGet();
	return this.xromaPektis[pektis];
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Skiniko.prototype.trexonInputSet = function(dom) {
	this.trexonInput = dom;
	return this;
}

Skiniko.prototype.trexonInputGet = function() {
	return this.trexonInput;
}

Skiniko.prototype.trexonInputFocus = function() {
	var dom = this.trexonInputGet();
	if (dom) dom.focus();
	return this;
}
